﻿using System;
using System.Reflection;

namespace Borg
{
    [Serializable]
    public class ExtensionEventHandler<T>
    {
        private readonly ExtensionMethodInvoker _invoker;
        private readonly Extension _extension;
        private readonly object _target;
        private readonly MethodInfo _method;

        protected internal ExtensionMethodInvoker Invoker
        {
            get { return _invoker; }
        }

        protected internal Extension Extension
        {
            get { return _extension; }
        }

        protected internal object Target
        {
            get { return _target; }
        }

        protected internal MethodInfo Method
        {
            get { return _method; }
        }

        public ExtensionEventHandler(ExtensionMethodInvoker invoker,
            Extension extension, object target, MethodInfo method)
        {
            if (invoker == null)
                throw new ArgumentNullException("invoker");
            
            if (extension == null)
                throw new ArgumentNullException("extension");
            
            if (target == null)
                throw new ArgumentNullException("target");
            
            if (method == null)
                throw new ArgumentNullException("method");
            
            var parameters = method.GetParameters();
            
            if (parameters.Length != 1 || !parameters[0].ParameterType.IsAssignableFrom(typeof(T)))
                throw new ArgumentException(
                    string.Format("Invalid signature: 'void Method({0} args)' expected", typeof(T).Name));

            _method = target.GetType().GetMethod(method.Name,
                BindingFlags.Instance | BindingFlags.Static |
                BindingFlags.Public | BindingFlags.NonPublic,
                null, new[] {parameters[0].ParameterType}, null);

            _invoker = invoker;
            _extension = extension;
            _target = target;
        }

        public void Invoke(T args)
        {
            InternalInvoke(args);
        }

        protected virtual void InternalInvoke(T args)
        {
            _invoker.Invoke(_extension, _target, _method, new object[] { args });
        }

        public override bool Equals(object obj)
        {
            if (this == obj)
                return true;
            
            if (!(obj is ExtensionEventHandler<T>))
                return false;
            
            var extensionEventHandler = (ExtensionEventHandler<T>)obj;
            
            return
                _extension.Equals(extensionEventHandler._extension) &&
                _target.Equals(extensionEventHandler._target) &&
                _method.Equals(extensionEventHandler._method);
        }

        public override int GetHashCode()
        {
            return
                _extension.GetHashCode() ^
                _target.GetHashCode() ^
                _method.GetHashCode();
        }
    }
}
